#include "board.h"
#include "app_config.h"
#include "components\battery_manager\battery_manager.h"
#include "components\lcd_drivers\lcd.h"
#include "polsys.h"

#ifdef _APP_DEBUG
#define SWD_DEBUG
#endif

// 用于指示本软件已经存在FLASH中的标志数据，不可以修改地址和值，否则BootLoader无法识别将不会跳转到本软件。
const u32 mAppFlag __attribute__((section(".ARM.__at_0x00002000"))) = 0XAA55A5A5;
// 预留存放固件长度的地方。
const u32 mAppLen __attribute__((section(".ARM.__at_0x00002004"))) = 0;

static polsys_task_t mTask;

#ifdef SWD_DEBUG
static bool mBmConnectStatus = false;
#endif

static vu16 mAdcKbValue = 0;
static vu16 mBatVoltage = 0; // 电池电压 mV
static bool mAwakenHint;     // 唤醒标记

static void Loop();
static void Board_GpioInit();
static void Board_GpioDeinit();

void Board_Init()
{
    // GPIO口初始化
    Gpio_Init();

    // 定时器初始化
    Tim_Init(TMR0);

    // MCU其他外设初始化
    Adc_Init();
    Spi_Init();

    // 刚上电时等待一次ADC采集结束
    Delay_ms(100);
    mBatVoltage = Adc_GetVDDVoltage();
    // if(mBatVoltage < 3350)  // 电量很低
    // {
    //     Board_EnterSleep();
    // }

    BM_Init(); // 确保之前电池电压可被正确获取

#ifndef SWD_DEBUG
    UART1_IO_INIT;
#else
    UART1_IO_DEINIT;
#endif

    // 看门狗初始化
    Wdt_Init();

    // 软定时器
    Polsys_InitTask(&mTask, Loop, 100);
}

u16 Board_GetVBat()
{
    mBatVoltage = Adc_GetVDDVoltage();
    return mBatVoltage;
}

// 负载检测专用
void Board_Delay(u8 t)
{
    u8 i, j;
    for (i = 0; i < t; i++)
        for (j = 0; j < 6; j++)
            ;
}

// 负载状态获取（硬件层面） true:已连接
bool Board_GetLoadStatus()
{
    bool bl;

    // 方案一：定时检测，检测时开启上拉，检测完毕关闭上拉（上拉电阻可以选择较小的阻值，抗干扰性好）
    // 负载接入检测功能，拉高OVAL
    OVAL_PULL_UP;
    // 检测负载延时
    Board_Delay(40);
    bl = (OVAL_PIN_IN == 0) ? 1 : 0;
    // 释放 OVAL
    OVAL_PULL_DOWN;
    Board_Delay(5);

    // // 方案二：一直开启上拉状态
    // OVAL_PULL_UP;
    // bl = (OVAL_PIN_IN == 0) ? 1 : 0;

    return bl;
}

// 休眠
void Board_Sleep()
{
    // 关闭ADC
    Adc_Disable();

    // 关闭看门狗
    Wdt_Disable();
    Wdt_SetTime(_MCU_AWAKEN_TIME);

    // 关闭所有开启的定时器
    Tim_Stop(TMR0);

    // 开启IO口唤醒
    WakeUp_Enable();

    mAwakenHint = false;

    // 等待按键唤醒 或者负载变化
    while (!_Btn_HwIsDown() && !_SWSD_IsValid() && !Board_GetDcIn() && !mAwakenHint && RHC_GetLoadStatus() == Board_GetLoadStatus())
    {
        // Delay_ms(10);
        Pow_Sleep();
    }

    // 关闭IO口中断
    WakeUp_Disable();

    // 重新启动定时器
    Tim_ClrIrqFlag(TMR0);
    Tim_Run(TMR0);

    // 开启ADC
    Adc_Enable();

    // 重新开启看门狗
    Wdt_SetTime(_WDT_TIME_INIT);
    Wdt_Clear();
    Wdt_Enable();
}

// 唤醒中断中调用本函数
void Board_AwakenIntEvent(void)
{
    mAwakenHint = true;
}

// LCD断电
void Board_LcdPowerOff(void)
{
    LCD_VDD_OFF;
    Board_VExtOff();
    Board_GpioDeinit();
}

// LCD上电
void Board_LcdPowerOn(void)
{
    Board_GpioInit();
    Board_VExtOn();
    LCD_VDD_ON;
}

// 复位
void Board_ResetToBl()
{
    FMC->LOCK = FMC_WRITE_KEY;
    FMC->CON &= 0XEF; // 选择复位后从 BOOT 区启动
    FMC->LOCK = 0x00;
    FMC_ErasePage((u32)&mAppFlag);
    SYS_ResetCPU();
    /* Trap the CPU */
    while (1)
        ;
}

////////////// 以下内部函数

static void Loop()
{
    Wdt_Clear();

#ifdef SWD_DEBUG
    if (BM_GetDCConnectStatus())
    {
        if (!mBmConnectStatus)
        {
            mBmConnectStatus = true;
            UART1_IO_INIT;
        }
    }
    else
    {
        if (mBmConnectStatus)
        {
            mBmConnectStatus = false;
            UART1_IO_DEINIT;
        }
    }
#endif
}

static void Board_GpioInit()
{
    SYS_SET_IOCFG(IOP47CFG, SYS_IOCFG_P47_MOSI);
    SYS_SET_IOCFG(IOP46CFG, SYS_IOCFG_P46_MISO);
    SYS_SET_IOCFG(IOP26CFG, SYS_IOCFG_P26_SCLK);

    // SPI FLASH
    FLASH_CS_PIN_OUT = 1;

    // SPI LCD
    LCD_CS_PIN_OUT = 1;
    LCD_RST_PIN_OUT = 1;
    LCD_DC_PIN_OUT = 1;

    // SPI
    SPI_CLK_PIN_OUT = 1;
    SPI_MOSI_PIN_OUT = 1;
}

static void Board_GpioDeinit()
{
    SYS_SET_IOCFG(IOP47CFG, SYS_IOCFG_P47_GPIO);
    SYS_SET_IOCFG(IOP46CFG, SYS_IOCFG_P46_GPIO);
    SYS_SET_IOCFG(IOP26CFG, SYS_IOCFG_P26_GPIO);

    // SPI FLASH
    FLASH_CS_PIN_OUT = 0;

    // SPI LCD
    LCD_CS_PIN_OUT = 0;
    LCD_RST_PIN_OUT = 0;
    LCD_DC_PIN_OUT = 0;

    // SPI
    SPI_CLK_PIN_OUT = 0;
    SPI_MOSI_PIN_OUT = 0;
}
